<?php
/**
 * @Author: liuhongbin
 * @Email: 514660094@qq.com
 * @Date: 2019-02-12 16:15:10
 * @Description:
 * @LastEditors: liuhongbin
 * @LastEditTime: xxx
 */

namespace app\common\model;

use think\db\Query;
use think\exception\ValidateException;
use think\facade\Config;
use think\helper\Str;
use think\Model;

abstract class BaseModel extends Model
{
    protected $autoWriteTimestamp = true;

    protected $dkWhere = [];

    /**
     * 查询条件
     * @var array
     */
    public $params = [];

    /**
     * 混入方法
     * @var array
     */
    public $mixMethods = [];

    /**
     * @var \think\Collection
     */
    public $data = null;

    /**
     * 表别名
     * @var string
     */
    public $alias = '';

    /**
     * 子页面字段
     * @var string|array
     */
    public $childFields = [];

    public function __construct(array $data = [])
    {
        parent::__construct($data);
        $this->params = request()->buildParams();
        $this->params['order'] = $this->params['order'] ?: $this->getPk() . ' DESC';
        $this->alias = parse_name(basename(str_replace('\\', '/', get_class($this))));
    }

    /**********************分页列表条件查询******************************/

    public function dkWhere($field, $op = null, $condition = null)
    {
        if (is_array($field)) {
            $this->dkWhere = array_merge($this->dkWhere, $field);
        } else {
            $this->dkWhere[] = [$field, $op, $condition];
        }
        return $this;
    }

    /**
     * 混入新的方法
     * @param string $name 方法名
     * @param array $params 参数值
     */
    public function mixMethod($name, ...$params)
    {
        $this->mixMethods[$name] = $params;
        return $this;
    }

    /**
     * 获取分页列表查询条件
     * @param $search
     * @return array
     */
    public function getPageListCondition($search = [])
    {
        if (!$search) {
            $search = $this->params['where'];
        }
        $ops = ['>', '>=', '<', '<=', '<>', 'like', 'notLike', 'in', 'notIn'];
        $where = [];

        foreach ($search as $key => $value) {
            if (!isset($value) || $value === "") {
                unset($search[$key]);
                continue;
            }
            if (is_array($value) && !Str::contains($key, $ops)) {
                if (Str::contains($key, "time")) {
                    $where[] = [$key, 'between', [($value[0] ?? 0) / 1000, ($value[1] ?? 0) / 1000]];
                } else {
                    $where[] = [$key, 'between', [$value[0] ?? 0, $value[1] ?? 0]];
                }
            } else {
                if (Str::contains($key, $ops)) {
                    foreach ($ops as $k => $op) {
                        if (Str::contains($key, $op)) {
                            if (Str::contains($key, ['like', 'notLike'])) $value = "%" . $value . "%";
                            $arr = explode(' ', $key);
                            if (count($arr) == 2) {
                                list($field, $_op) = $arr;
                                if ($field && $_op == $op) {
                                    $where[] = [$field, $op, $value];
                                }
                            } else {
                                $where[] = [$key, '=', $value];
                            }
                        }
                    }
                } else {
                    $where[] = [$key, '=', $value];
                }
            }
        }
        return $where;
    }

    /**
     * 获取分页列表条件模型
     * @param array $params 前端条件
     * @param bool $paginate 是否分页
     * @return BaseModel
     */
    public function getPageListModel($params = [], $paginate = true)
    {
        $params = $params ?: $this->params;
        $where = $this->getPageListCondition($params['where']);
        $model = $this->where($where);
        if (count($this->dkWhere) > 0) {
            foreach ($this->dkWhere as $key => $value) {
                if (count($value) == 2) {
                    $model->where($value[0], $value[1]);
                } elseif (count($value) == 3) {
                    $model->where($value[0], $value[1], $value[2]);
                }
            }
        }
        if ($paginate) {
            $model = $model->page($params['page'] ?? 0)->limit($params['size'] ?? 10);
        }
        if($params['order']){
            $model->order($params['order']);
        }
        return $model;
    }

    public function withoutDefaultOrder()
    {
        $this->params['order'] = '';
        return $this;
    }

    /**
     * 获取分页列表数据
     * @param array $params
     * @return \think\Collection
     */
    public function getPageListData($mix = [], $params = [])
    {
        $model = $this->mixPageListModel($this->getPageListModel($params), $mix);
        return $model->select();
    }

    /**
     * 获取分页列表总数
     * @param array $params
     * @return int
     */
    public function getPageListCount($mix = [], $params = [])
    {
        $model = $this->mixPageListModel($this->getPageListModel($params, false), $mix);
        return $model->count();
    }

/**
 * 获取分页列表
 * @param array|callable $mix 可以是回调函数或数组，用于对查询模型进行额外操作
 *   - 如果是回调函数，函数将接收一个查询模型对象作为参数，可在函数内部对模型进行自定义操作
 *     示例: function($query) { $query->where('status', 1); }
 *   - 如果是数组，数组的键表示要调用的模型方法名，值表示传递给该方法的参数
 *     示例: ['where' => ['status', 1]] 或 ['field' => 'id,name']
 *     特殊语法: 以 $ 开头的键名会被去掉前缀后直接调用方法
 *     示例: ['$join' => [['table', 'condition']]]
 * @param array $params 查询参数
 * @return array 返回包含数据和总数的数组
 */
public function getPageList($mix = [], $params = [])
{
    return [
        'data'  => $this->getPageListData($mix, $params),
        'count' => $this->getPageListCount($mix, $params)
    ];
}

    /**
     * 混入参数到模型中
     * @param Model|Query $model
     * @param array|callable |mixed $mix
     * @return Model|Query
     */
    public function mixPageListModel( $model, $mix = [])
    {
        if (is_callable($mix)) {
            $mix($model);
            return $model;
        } else {
            $mix = array_merge($mix, $this->mixMethods);
            foreach ($mix as $key => $value) {
                if (is_array($value) && strpos($key, "$") !== 0) {
                    // $value被分割当做参数传入
                    $model->$key(...$value);
                } else {
                    // $value直接当做参数传入
                    $key = str_replace("$", "", $key);
                    $model->$key($value);
                }
            }
            return $model;
        }

    }

    /**********************分页列表条件查询   END******************************/


    /**
     * 模型验证器
     * @access public
     * @param array|object $data 数据
     * @param string|null $scene 场景
     * @param bool $isFilter 过滤
     * @param bool $showMsg 显示错误信息
     * @return array|boolean
     */
    public function dataValidate(array &$data, $scene = null, $isFilter = false, $showMsg = false)
    {
        try {
            // 验证器和模型名称相同
            $class = '\\app\\common\\validate\\' . $this->getName();
            if ($scene) {
                //存在场景
                $validate_class = new $class();
                $validate_class->changeSceneToRule($data, $scene, $isFilter, $this->getPk());
            } else {
                //不存在场景
                $validate_class = validate($class);
            }
            if ($isFilter) {
                $keys = $validate_class->getRuleKey();
                foreach ($data as $key => $value) {
                    if (!in_array($key, $keys, true)) {
                        unset($data[$key]);
                    }
                }
                unset($key, $value);
            }
            $validate_class->failException(true)->check($data);
        } catch (ValidateException $e) {
            return $showMsg ? ['code' => -1, 'msg' => $e->getMessage()] : false;
        }
        return $showMsg ? ['code' => 1] : true;
    }

    /**
     * 操作成功返回值函数
     * @param string $msg
     * @param array $data
     * @param string $code
     * @return array
     */
    public function success($msg = '', $data = [], $other = [], $code = 'SUCCESS')
    {
        return success($msg, $data, $other, $code);
    }

    /**
     * 操作失败返回值函数
     * @param string $msg
     * @param array $data
     * @param string $code
     * @return array
     */
    public function error($msg = '', $data = [], $code = 'FAIL')
    {
        return error($msg, $data, $code);
    }

    /**
     * 检测是否存在相同值
     * @access public
     * @param array $where 查询条件
     * @return bool false:不存在
     */
    public static function checkUnique(array $where)
    {
        if (empty($where)) {
            return true;
        }

        $count = self::where($where)->count();
        if (is_numeric($count) && $count <= 0) {
            return false;
        }

        return true;
    }


    /*****************************范围查询*****************************************/
    /**
     * 判断平台ID
     * @param $query
     * @return void
     */
    public function scopePlaid(Query $query)
    {
        $query->where('plaid', get_plaid());
    }

}
